import {IDisposable} from "./lifecycle";
import * as types from 'egret/base/common/types';

export class CallbackList {
    private _callbackArr: { callback: Function, context: any, priority: number }[] = [];

    public add(callback: Function, context: any = null, bucket?: IDisposable[], priority?: number): void {
        this._callbackArr.push({
            callback, context, priority: priority || 0
        });

        this._callbackArr.sort((a, b) => {
            return b.priority - a.priority;
        })

        if (Array.isArray(bucket)) {
            bucket.push({dispose: () => this.remove(callback, context)});
        }
    }

    public remove(callback: Function, context: any = null): void {
        if (this._callbackArr.length == 0) {
            return;
        }
        let foundCallbackWithDifferentContext = false;
        for (var i = 0, len = this._callbackArr.length; i < len; i++) {
            if (this._callbackArr[i].callback === callback) {
                if (this._callbackArr[i].context === context) {
                    // callback & context match => remove it
                    this._callbackArr.splice(i, 1);
                    return;
                } else {
                    foundCallbackWithDifferentContext = true;
                }
            }
        }

        if (foundCallbackWithDifferentContext) {
            throw new Error('When adding a listener with a context, you should remove it with the same context');
        }
    }

    public invoke(...args: any[]): any[] {
        if (this._callbackArr.length == 0) {
            return undefined;
        }
        let arr = this._callbackArr.slice(0);

        const ret: any[] = [];

        for (var i = 0, len = arr.length; i < len; i++) {
            ret.push(arr[i].callback.apply(arr[i].context, args));
        }
        return ret;
    }

    public pull(...args: any[]): any {
        if (this._callbackArr.length == 0) {
            return args[0];
        }

        let arr = this._callbackArr.slice(0);

        for (var i = 0, len = arr.length; i < len; i++) {
            var ret = arr[i].callback.apply(arr[i].context, args);
            if (!types.isUndefined(ret)) {
                args[0] = ret;
            }
        }

        return args[0];
    }

    public isEmpty(): boolean {
        return this._callbackArr.length === 0;
    }

    public entries(): [Function, any][] {
        if (this._callbackArr.length == 0) {
            return [];
        }

        return this._callbackArr.map((fn, idx) => {
            return <[Function, any]>[fn.callback, fn.callback];
        });
    }

    public dispose(): void {
        this._callbackArr.length = 0;
    }
}
